import 'dart:io';

import 'package:flame/components.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:freedom/const/game_const.dart';
import 'package:freedom/entity/restful.dart';
import 'package:freedom/page/game_index.dart';
import 'package:freedom/page/mahjong/bloc/room_bloc.dart';
import 'package:freedom/page/mahjong/bloc/room_info.dart';
import 'package:freedom/page/mahjong/bloc/user_state.dart';
import 'package:freedom/page/mahjong/common/common_card.dart';
import 'package:freedom/page/mahjong/common/message_content.dart';
import 'package:freedom/page/mahjong/dialog/record_dialog.dart';
import 'package:freedom/page/mahjong/event/user_state_event.dart';
import 'package:freedom/page/mahjong/export_area/abstract_component.dart';
import 'package:freedom/page/mahjong/hand_area/abstract_card_area.dart';
import 'package:freedom/page/mahjong/keneck/head_image_component.dart';
import 'package:freedom/page/mahjong/mahjong_game.dart';
import 'package:freedom/page/mahjong/provider/mahjong_room_info.dart';
import 'package:freedom/page/mahjong/util/abstrcat_mahjong_func.dart';
import 'package:freedom/page/mahjong/util/mahjong_util.dart';
import 'package:freedom/page/mahjong/util/wz_mahjong_func.dart';
import 'package:freedom/util/api_router.dart';
import 'package:freedom/util/customer_dialog.dart';
import 'package:freedom/util/event_bus.dart';
import 'package:freedom/util/http.dart';
import 'package:freedom/util/navigator_provider.dart';
import 'package:freedom/util/socket_server.dart';
import 'package:provider/provider.dart';
import '../keneck/abstract_action.dart';
import '../util/user_location.dart';

class MahjongOverlay {
  static const String MESSAGE_OVERLAY = "buildSendMessage";
  static const String CARD_CHANGE_OVERLAY = "buildCardChange";
  static const String MAHJONG_GAMEING_OVER = "MAHJONG_GAMEING_OVER";
  static const String MAHJONG_HU_CARD = "MAHJONG_HU_CARD";
  static const String userInfoDialog = "userInfoDialog";
  static const String huCardDialog = "huCardDialog";
  static const String swapCardTipsDialog = "swapCardTipsDialog";
  static const String swapCardInfo = "swapCardInfo";
  static const String multipmleChoiceCardDialog = "multipmleChoiceCardDialog";
  static const String balanceDialog = "balanceDialog";
}

class DiskUserState {
  int userId;
  Direction direction;
  AbstractExport export;
  AbstractHand hand;
  HeadImageComponent head;
  DiskUserState(this.userId, this.direction, this.export, this.hand, this.head);
  UserState get userState => MahjongContext.getUserState(direction)!;
}

class MahjongRoomStatus {
  //, "准备状态,未开始")
  static const int creating = 1;
  //, "玩家准备")
  static const int reading = 2;
  //, "游戏进行中")
  static const int gaming = 3;
  //(4, "结算状态")
  static const int balance = 4;
  //(5, "游戏结束")
  static const int over = 5;
}

class MahjongContext {
  // 麻将单例对象
  static MahjongContext? _instance;
  // game 对象
  MahjongIndexGame? _mahjongIndexGame;
  // 房间信息
  RoomInfo? _roomInfo;
  // 房间策略
  final Map<String, AbstractMahjongFunc> _mahjongFunc = {};
  // 是否为播放录像模式
  bool _playbackState = false;

  bool get getPlayback => _playbackState;

  set setPlayback(bool state) => _playbackState = state;

  // 获取桌面上所有的牌
  Map<int, int> getDiskAllCard() {
    List<int> diskAllCard = [];
    for (var userDisk in _diskUserInfoId.entries) {
      // 拿到所有出牌区的所有牌
      for (var expChild in userDisk.value.export.children) {
        if (expChild is CommonCard) {
          diskAllCard.add(expChild.cardCode);
        }
      }
      // 拿到碰杠 所有的牌
      userDisk.value.hand.currentUserState.openDeal?.forEach((openDeal) {
        int index = openDeal.getCardCount();
        while (index > 0) {
          diskAllCard.add(openDeal.card);
          index--;
        }
      });
      // 拿到用户的手牌,其它人是不可见的 这里只有当前人会返回数组
      diskAllCard
          .addAll(userDisk.value.hand.currentUserState.getUserHandCards());
    }
    // 去重
    List<int> uniqueElements = diskAllCard.toSet().toList();
    // 统计每个元素在原列表中出现的次数
    Map<int, int> resultMap = {
      for (var element in uniqueElements)
        element: diskAllCard.where((e) => e == element).length
    };
    return resultMap;
  }

  /* ======================用户相关开始========================= */
  // 存储用户数据状态  碰杠胡 手牌  等
  final Map<Direction, UserState> _diskUserState = {};
  // 设置用户数据
  static void setUserState(Direction direction, UserState userState) {
    MahjongContext()._diskUserState[direction] = userState;
  }

  // 获取用户数据
  static UserState? getUserState(Direction direction) =>
      MahjongContext()._diskUserState[direction];

  static void initRoom(RoomInfo roomInfo) {
    MahjongContext()._roomInfo = roomInfo;
  }

  static RoomInfo get roomInfo => MahjongContext()._roomInfo!;

  static set syncVersion(int version) {
    MahjongContext()._roomInfo?.gameInfo.synVersion = version;
  }

  static int get userCount =>
      MahjongContext().getRoomInfo()?.rule?.userCount ?? 0;

  static int get syncVersion =>
      MahjongContext()._roomInfo?.gameInfo.synVersion ?? 0;

  static set roomInfo(RoomInfo param) {
    MahjongContext()._roomInfo = param;
  }

  static void setExportCardUser(int uid, int cardCode) {
    var gameInfo = MahjongContext.roomInfo.gameInfo;
    gameInfo.exportUserId = uid;
    gameInfo.exportCard = cardCode;
  }

  static UserState? get currentUser => getUserState(Direction.BOTTOM);

  final Map<int, DiskUserState> _diskUserInfoId = {};

  //屏幕的宽度和高度
  double screenWidth = ScreenUtil().screenWidth,
      screenHeight = ScreenUtil().screenHeight;

  // 每一个用户的事件订阅
  final Map<int, XEventBus> _userEventCache = {};

  final XEventBus _roomBus = XEventBus();

  Map<int, XEventBus> get getUserEventCache => _userEventCache;
  // 获取用户方向
  static Direction getUserDirection(int userId) {
    return MahjongContext()._diskUserInfoId[userId]?.direction ??
        Direction.BOTTOM;
  }

  // 获取用户出牌区
  static AbstractHand getUserHandArea(int userId) {
    return MahjongContext()._diskUserInfoId[userId]!.hand;
  }

  // 获取用户手牌区
  static AbstractExport getUserExportArea(int userId) {
    return MahjongContext()._diskUserInfoId[userId]!.export;
  }

  static Map<int, DiskUserState> get diskAllUser =>
      MahjongContext()._diskUserInfoId;

  static DiskUserState? getUserStateById(int userId) =>
      MahjongContext.diskAllUser[userId];
  static DiskUserState? getUserStateBySeq(int seq) =>
      MahjongContext.diskAllUser.values
          .singleWhere((element) => element.userState.seq == seq);

  static DiskUserState? getUserStateByDir(Direction direction) =>
      MahjongContext.diskAllUser.values
          .singleWhere((element) => element.direction == direction);

  // 初始化用户事件
  // 每次房间加载就会执行这个函数
  // 但是bus 可不要这样组件onLoad 只会加载一次
  // 如果重复执行的话会覆盖 导致事件订阅丢失
  static void initUserEvent(UserState userState,
      {required Direction direction,
      required AbstractHand hand,
      required AbstractExport export,
      required HeadImageComponent head}) {
    var diskUserState =
        DiskUserState(userState.userId, direction, export, hand, head);
    var toIdInfo = MahjongContext()._diskUserInfoId[userState.userId];
    if (toIdInfo == null) {
      MahjongContext()._diskUserInfoId[userState.userId] = diskUserState;
    }
  }

  // 注册用户事件
  static void registerUserFunc<T>(int userId, ISubscriber<T> subscriber) {
    var event = MahjongContext().getUserEventCache[userId];
    if (event == null) {
      event = EventBusProvider.newEventBus;
      MahjongContext().getUserEventCache[userId] = event;
    }
    event.register(subscriber);
  }

  /// 发布用户事件

  static void postUserEvent<T>(int userId, T data) async {
    var event = MahjongContext().getUserEventCache[userId];
    event?.post<T>(data);
  }

  /// 注册房间

  static void registerRoomFunc<T>(ISubscriber<T> subscriber) {
    MahjongContext()._roomBus.register(subscriber);
  }

  /// 发布房间事件

  static void postRoomEvent<T>(T data) async {
    MahjongContext()._roomBus.post(data);
  }

  MahjongContext._();

  AbstractMahjongFunc getFunc({String? type}) {
    type ??= _roomInfo?.type;
    return _mahjongFunc[type] ?? WzMahjongFunc();
  }

  void setRoomInfo(RoomInfo roomInfo) {
    _roomInfo = roomInfo;
  }

  UserState? getBanker() {
    if (_roomInfo == null) {
      return null;
    }
    return _roomInfo?.gameInfo.allUser
        ?.singleWhere((element) => element.banker);
  }

  /// 全局单例
  ///

  factory MahjongContext() {
    _instance ??= MahjongContext._();
    return _instance!;
  }

  /// 调整胡牌状态信息

  void reInitHuCardTips(bool show,
      {List<int>? userAllCards, int? exportCard, bool checkHave = false}) {
    if (_playbackState) {
      return;
    }
    if (!show) {
      _mahjongIndexGame?.roomBloc?.add(UpdateUserTipsState(show));
      Provider.of<MahjongRoomInfoProvider>(NavigatorProvider.navigatorContext,
              listen: false)
          .cleanTips();
      return;
    }
    if (userAllCards == null && currentUser != null) {
      userAllCards = currentUser!.getUserHandCards();
    } else if (userAllCards == null && currentUser == null) {
      userAllCards = [];
    }
    userAllCards = userAllCards ?? [];
    List<int> allHuCards = getFunc().checkHuCard(
        MahjongUtil.removeCard(
            userAllCards, exportCard == null ? [] : [exportCard]),
        MahjongContext().getRoomInfo());
    if (checkHave) {
      show = allHuCards.isNotEmpty;
    } // 碰杠 都要重新计算
    Provider.of<MahjongRoomInfoProvider>(NavigatorProvider.navigatorContext,
            listen: false)
        .updateState(show, allHuCards);
    _mahjongIndexGame?.roomBloc?.add(UpdateUserTipsState(show));
  }

  /**
   * 一下状态回放模式 都不开启
   */

  void updateSwapCardIn(String? swapCardIn) {
    if (_playbackState) {
      return;
    }
    Provider.of<MahjongRoomInfoProvider>(NavigatorProvider.navigatorContext,
            listen: false)
        .updateSwapCardIn(swapCardIn);
  }

  void updateSwapCard(String? swapCardIn, String? swapCardOut) {
    if (_playbackState) {
      return;
    }
    Provider.of<MahjongRoomInfoProvider>(NavigatorProvider.navigatorContext,
            listen: false)
        .updateSwapCard(swapCardIn, swapCardOut);
  }

  //修改拿出换牌
  void updateSwapCardOut(String? swapCardOut) {
    if (_playbackState) {
      return;
    }
    Provider.of<MahjongRoomInfoProvider>(NavigatorProvider.navigatorContext,
            listen: false)
        .updateSwapCardOut(swapCardOut);
  }

  void cleanHuCardTips() {
    if (_playbackState) {
      return;
    }
    Provider.of<MahjongRoomInfoProvider>(NavigatorProvider.navigatorContext,
            listen: false)
        .cleanTips();
  }

  bool isCurrentUser(int sourceId) {
    return currentUser?.userId == sourceId;
  }

  /// 注册游戏对象
  ///
  /// 初始化策略对象
  ///
  /// 清理缓存

  void init(MahjongIndexGame mahjongIndexGame) {
    cleanCache();
    _mahjongIndexGame = mahjongIndexGame;
    _playbackState = false;
    _mahjongFunc[RoomType.WZ_MAHJONG] = WzMahjongFunc();
  }

  /// 当房间重新加载的时候
  /// 一定要清理 缓存
  void cleanCache() {
    // 清理按ID获取用户
    MahjongContext()._diskUserInfoId.clear();
    // 清理事件订阅
    MahjongContext()._userEventCache.forEach((key, value) {
      for (var element in value.map.entries) {
        element.value.clear();
      }
      value.map = {};
    });
    MahjongContext()._userEventCache.clear();
    MahjongContext()._userEventCache.clear();
    MahjongContext()._diskUserState.clear();
    MahjongContext()._mahjongFunc.clear();
  }

  RoomInfo? getRoomInfo() {
    return _roomInfo;
  }

  /// 显示游戏结算内容
  void showOverlay(String overlayName) {
    var overlays = mahjongGameIndex.findGame()!.overlays;
    closeOverlay(overlayName);
    overlays.add(overlayName);
  }

  void closeOverlay(String overlayName) {
    var overlays = mahjongGameIndex.findGame()!.overlays;
    overlays.remove(overlayName);
  }

  /// 显示游戏结算内容
  void showGameOverWidget(RoomInfo roomInfo) {
    mahjongGameIndex.gameInfo = roomInfo.gameInfo;
    var overlays = mahjongGameIndex.findGame()!.overlays;
    if (!overlays.isActive(MahjongOverlay.MAHJONG_GAMEING_OVER)) {
      overlays.add(MahjongOverlay.MAHJONG_GAMEING_OVER);
    } else {
      overlays.remove(MahjongOverlay.MAHJONG_GAMEING_OVER);
    }
  }

  /// 游戏结束
  void gameOver() {
    SocketServer().closeSocket();
    mahjongGameIndex.scheduleController?.close();
    cleanCache();
  }

  void gameOverAndToIndex() {
    gameOver();
    CustomerDialog.closeAllLoading();
    NavigatorProvider.pushAndRemoveUntil(const GameIndex());
  }

  void showGameBalanceWidget(RoomInfo roomInfo) {
    MahjongContext.roomInfo = roomInfo;
    if ((roomInfo.status == 4 &&
            (currentUser?.checkWaitActionExits(MessageAction.PREPARE) ??
                true)) ||
        roomInfo.status == 5) {
      //这里有个顺序问题  当前用户应该在前面
      var userInfo = roomInfo.gameInfo.allUser
          ?.singleWhere((element) => element.userId == currentUser?.userId);
      roomInfo.gameInfo.allUser
          ?.removeWhere((element) => element.userId == currentUser?.userId);
      if (userInfo != null) {
        roomInfo.gameInfo.allUser?.insert(0, userInfo);
      }
      showOverlay(MahjongOverlay.balanceDialog);
    } else {
      closeOverlay(MahjongOverlay.balanceDialog);
    }
  }

  //所有事件的订阅者
  List<AbstractAction>? subscribe;

  static List<String> actionImageCode = [
    "PREPARE",
    "BAR",
    "CALL",
    "HU",
    "BUMP",
    "PASS"
  ];

  void initScreen(screenWidth, screenHeight) {
    this.screenHeight = screenHeight;
    this.screenWidth = screenWidth;
  }

  /// 注册订阅者

  void register(AbstractAction action) {
    subscribe ??= [];
    subscribe!.add(action);
  }

  /// 广播
  void broadcast(data) {
    subscribe ??= [];
    for (var element in subscribe!) {
      element.doAction(data);
    }
  }

  // 上下   宽/长比例
  final double btCardWHRatio = 1.41;
  final double bottomCardTopRatio = 1.28;

  // 左边手牌 高度 上移比例
  final double leftHandCardSegRatio = 0.51;

  // 左边手牌 宽和高的比例
  final double leftHandCardWHRatio = 0.4;

  // 斜面与正面的比例
  final double lrHandCardImageHBRatio = 0.75;

  // 左右出牌 斜面跟正面的比例
  final double lrExportCardBgAndImageRatio = 0.63;

  // 棋牌 1-27图片比例
  final double cardImageWHRatio = 0.78;

  //  上下出牌的对角 与总高度的比例
  final double exportDiagonalRatio = 0.773;

  final double topCardWHRatio = 0.66;

  final double firstCardMargin = 1.2;

  final double cardTipsWHRatio = 0.889;

  final double leExportXYRatio = 1.13;

  final double lrTbByExportRatio = 0.85;

  /// 获取刘海屏的高度,如果有高度直接返回
  /// 如果没有返回头像一半的宽度

  double get marginTop => ScreenUtil().statusBarHeight > 0
      ? ScreenUtil().statusBarHeight
      : headImageHeight / 2;

  // 用户手牌的大小
  Vector2 get bottomHandCard =>
      Vector2(screenWidth / 16, screenWidth / 16 * btCardWHRatio);

  // 交换的牌大小
  Vector2 get lrSwapCardSize =>
      Vector2(bottomHandCard.x / 2, bottomHandCard.x / 2 * cardTipsWHRatio);

// 这里的菜单大小  直接是屏幕的高度
  Vector2 get menuSize => Vector2(bottomHandCard.x * 2 / 3, screenHeight);

  // 这里的菜单定位 根据 右边的头像 定位 根据头像居中
  Vector2 get menuPosition => Vector2(
      rImagePoint.x + (headImageHeight - menuSize.x) / 2 - marginTop, 0);

  // 用户头像大小
  double get headImageHeight => bottomHandCard.x * 0.8;

  ///  底部用户出牌的大小
  /// 这里使用屏幕的横向高度 来做算法,避免不同手机长度不一样导致只显示2层
  Vector2 get exportCardSize {
    // 这里最好 不要这么干 bottomHandCard * 0.6;
    var height = (bottomHandCardAreaPoint.y - centerDiskHeight) / 12;

    return Vector2(height, height * btCardWHRatio);
    // bottomHandCard * 0.6;
  }

  Vector2 get topTBCardSize =>
      Vector2(topHandCardSize.y / btCardWHRatio, topHandCardSize.y);

  Vector2 get bottomTBCardSize => bottomHandCard * 0.8;

  Vector2 get lrTBCardSize => Vector2(exportCardSize.x * lrTbByExportRatio,
      exportCardSize.x * leExportXYRatio * lrTbByExportRatio);

  //顶部出牌大小
  Vector2 get topExportCardSize => exportCardSize * 0.9;

  Vector2 get topHandCardSize => bottomHandCard * 0.4;

  /// 左右两边出牌大小与底部出牌大小比例
  final double lrExportRatioByBottomExport = 0.9;

/*
exportCardSize.x * leExportXYRatio
Vector2(
      exportCardSize.x * lrExportRatioByBottomExport,
      exportCardSize.x * lrExportRatioByBottomExport * leExportXYRatio)*/
  Vector2 get lrExportCardSize =>
      Vector2(exportCardSize.x, exportCardSize.x * leExportXYRatio);

  // BOTTOM出牌区域大小
  Vector2 get bottomExportAreaSize =>
      Vector2(screenWidth / 3.5, screenHeight / 4);

  Vector2 get bottomExportAreaPoint =>
      Vector2(screenWidth / 2, 2 * screenHeight / 3);

  // 用户手牌定位

  Vector2 get bottomHandCardAreaPoint => Vector2(
      screenWidth / 2 - bottomHandCardAreaSize.x / 2,
      screenHeight - bottomHandCard.y - screenHeight * 0.01);
// 用户手牌大小
  Vector2 get bottomHandCardAreaSize =>
      Vector2(bottomHandCard.x * 14, bottomHandCard.y);

  // TOP出牌区域大小
  Vector2 get topExportAreaSize => Vector2(screenWidth / 3.5, screenHeight / 4);

  Vector2 get topExportAreaPoint => Vector2(screenWidth / 2, screenHeight / 4);

  Vector2 get topHandAreaSize =>
      Vector2(topHandCardSize.x * 13.5, topHandCardSize.y);

  Vector2 get topHandAreaPoint =>
      Vector2(screenWidth / 2 - topHandAreaSize.x / 2, screenHeight * 0.01);

  // LEFT出牌区域大小
  Vector2 get leftExportAreaSize =>
      Vector2(topExportAreaSize.x / 3 * 2, topExportAreaSize.y);

  // 居中 注意这里局中不是桌面居中是跟骰子居中
  Vector2 get leftExportAreaPoint => Vector2(
      screenWidth / 2 -
          topExportAreaSize.x / 2 -
          topExportAreaSize.y / 2 -
          exportCardSize.x,
      diskDirectionPoint.y + centerDiskHeight / 2);

  /// 左手手牌总高度
  double get leftHandHeight => bImagePoint.y - headImageHeight / 2;
  double get leftCardTopHeight => leftHandHeight / 18;

  //LEFT手牌大小 是底部的1/4
  Vector2 get lrCardSize =>
      Vector2(leftCardTopHeight * 0.93, leftCardTopHeight * 2.15);

  /* Vector2 get lrCardSize =>
      Vector2((screenHeight - bImagePoint.y) / 14, screenHeight / 14); */

  //LEFT手牌区域大小
  Vector2 get lrCardAreaSize => Vector2(lrCardSize.x, leftHandHeight);

  //距离顶部一张手牌的大小
  Vector2 get leftCardAreaPoint =>
      Vector2(lImagePoint.x + headImageHeight + lrCardAreaSize.x * 2, 0);

  // 左边头像的位置
  Vector2 get lImagePoint => Vector2(marginTop, screenHeight / 4);

  Vector2 get bImagePoint =>
      Vector2(marginTop, bottomHandCardAreaPoint.y - headImageHeight * 3 / 2);

  // 当前用户操作区域
  Vector2 get bottomGamingActionAreaSize =>
      Vector2(bottomHandCard.y * 4 / 5 * 4, bottomHandCard.y * 4 / 5);

  Vector2 get bottomGamingActionAreaPoint => Vector2(
      menuPosition.x - bottomGamingActionAreaSize.x - menuSize.x / 2,
      bottomHandCardAreaPoint.y -
          bottomHandCardAreaSize.y / 2 -
          bottomGamingActionAreaSize.y / 2);

  //当前用户 操作按钮 碰杠胡

  Vector2 get bottomGamingActionSize =>
      Vector2(bottomGamingActionAreaSize.y, bottomGamingActionAreaSize.y);

  Vector2 get rImagePoint =>
      Vector2(screenWidth - marginTop - headImageHeight, screenHeight / 4);

  // 顶部头像的位置
  Vector2 get tImagePoint => Vector2(
      topHandAreaPoint.x - headImageHeight * 2 - topHandCardSize.x,
      topHandAreaPoint.y);

  // RIGHT出牌区域大小
  Vector2 get rightExportAreaSize =>
      Vector2(topExportAreaSize.x / 3 * 2, topExportAreaSize.y);

  Vector2 get rightExportAreaPoint => Vector2(
      screenWidth / 2 +
          topExportAreaSize.x / 2 +
          topExportAreaSize.y / 2 +
          exportCardSize.x,
      diskDirectionPoint.y + centerDiskHeight / 2);

  //Right手牌区域
  Vector2 get rightHandCardAreaSize => Vector2(lrCardSize.x, lrCardSize.y * 16);

  // 一些桌面信息计算

  Vector2 get rightHandCardAreaPoint =>
      Vector2(rImagePoint.x - lrCardSize.x * 3, lrCardSize.x);

  double get centerAreaHeight =>
      bottomExportAreaPoint.y -
      topExportAreaPoint.y -
      bottomExportAreaSize.y / 2 -
      topExportAreaSize.y / 2;

  double get centerDiskHeight => centerAreaHeight * 2 / 3;

  double get gameCenterY =>
      topExportAreaPoint.y + topExportAreaSize.y / 2 + centerAreaHeight / 2;

  //中间方向
  Vector2 get diskDirectionPoint => Vector2(
      screenWidth / 2 - centerDiskHeight / 2,
      gameCenterY - centerDiskHeight / 2);

  static MahjongIndexGame get mahjongGameIndex =>
      MahjongContext()._mahjongIndexGame!;
}
